import threading
import traceback
from typing import Literal, Any

from config import Config

class TaskManager:
    def __init__(self, task_name:str) -> None:
        self.tasks = {}
        self.active_task_count = 0
        self.max_task = 0
        self.task_name = task_name

    def init(self, config:Config) -> None:
        config.define_key("{}_max_task".format(self.task_name),50)

    def config(self, config:dict) -> None:
        self.max_task = config["{}_max_task".format(self.task_name)]

    def add_task(self, numeric_id:int, task) -> Literal["too_many_tasks","task_exists","success"]:
        status = self.check_status(numeric_id)
        if status != "not_found": return "task_exists"
        if self.active_task_count >= self.max_task: return "too_many_tasks"
        self._add_task(numeric_id, task)
        return "success"

    def _add_task(self, numeric_id:int, task) -> None:
        t = Task(task, numeric_id, self)
        self.tasks[numeric_id] = t
    
    def run_task(self, numeric_id:int, args:tuple=(), kwargs:dict={}) -> None:
        self.tasks[numeric_id].run(args, kwargs)

    def check_status(self, numeric_id:int) -> Literal["not_found","ready","running","end"]:
        task = self.tasks.get(numeric_id, None)
        if not task: return "not_found"
        
        return task.status
    
    def get_result(self, numeric_id:int) -> Any:
        task = self.tasks[numeric_id]
        return task.result
    
    def get_error(self, numeric_id:int):
        task = self.tasks[numeric_id]
        return task.error

    def clean_task(self, numeric_id:int) -> None:
        self.tasks.pop(numeric_id)

class Task:
    """
    run a task in a thread
    """
    def __init__(self, task,id:int, mgr:TaskManager) -> None:
        self.status = "ready"
        self.task = task
        self.mgr = mgr
        self.result = None
        self.error = None

    def run(self, args=None, kwargs=None) -> None:
        if not args: args = []
        if not kwargs: kwargs = {}
        def task_func(_args, _kwargs):
            self.status = "running"
            self.mgr.active_task_count += 1
            try:
                self.result = self.task(*_args,**_kwargs)
                self.status = "end"
            except Exception as e:
                self.status = "error"
                self.error = e, traceback.format_exc()
                
            self.mgr.active_task_count -= 1         
        threading.Thread(target=task_func,kwargs={
            "_args": args,
            "_kwargs": kwargs
        }).start()